package com.lx.spring.reflect;


import com.lx.spring.config.bean.Person;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.Properties;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;

public class ReflectDemo1 {

    @Test
    public void test1() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Class pC = Person.class;

        //获取类对象的全参构造器
        // 抛出无此方法异常
        Constructor cons = pC.getConstructor(String.class, Integer.class);

        Object obj = cons.newInstance("大宝", 16);
        Person p = (Person) obj;
        System.out.println(p);

        //调用属性
        Field age = pC.getDeclaredField("age");
        age.setAccessible(true);
        age.set(p, 10);

        System.out.println(p);

        // 调用方法
        Method method = pC.getDeclaredMethod("show");
        method.invoke(p);


    }


    //获取class实例的方式
    @Test
    public void test2() throws IllegalAccessException, InstantiationException, ClassNotFoundException {

        //方式1: 调用运行时类的属性 .class
        Class<Person> pClass1 = Person.class;
        System.out.println(pClass1);
        Person p1 = pClass1.newInstance();
        System.out.println(p1);

        //方式2: 通过运行时类的对象,调用 getClass()
        Person p2 = new Person();
        Class pClass2 = p2.getClass();
        System.out.println(pClass2);

        //方式3: 调用Class的静态方法 forName(String class-full-name)
        Class pClass3 = Class.forName("com.lx.spring.config.bean.Person");
//        pClass3 = Class.forName("java.lang.String");
        System.out.println(pClass3);

        System.out.println(pClass1 == pClass2);
        System.out.println(pClass1 == pClass3);


        // 方式4: 使用类的加载器 ClassLoader
        ClassLoader classLoader = ReflectDemo1.class.getClassLoader();
        Class pClass4 = classLoader.loadClass("com.lx.spring.config.bean.Person");
        System.out.println(pClass4);

        System.out.println(pClass1 == pClass4);

    }

    //类的种类
    @Test
    public void test3() {
        Class c1 = Object.class;
        Class c2 = Comparable.class;
        Class c3 = String[].class;
        Class c4 = int[][].class;
        Class c5 = ElementType.class;
        Class c6 = Override.class;
        Class c7 = int.class;
        Class c8 = void.class;
        Class c9 = Void.class;
        Class c10 = Class.class;


        int[] a = new int[10];
        int[] b = new int[100];
        Class c11 = a.getClass();
        Class c12 = b.getClass();

        System.out.println(c11 == c12);
    }


    //类加载与ClassLoader的理解
    @Test
    public void test4() {

        System.out.println(A.m);

        //对于自定义类 使用系统类加载器
        ClassLoader classLoader1 = ReflectDemo1.class.getClassLoader();
        System.out.println(classLoader1);

        // 获取父类, 获取扩展类加载器
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);

        // 但是无法获取引导类加载器
        //引导类加载器主要负责加载java的核心类库,无法加载自定义类
        ClassLoader classLoader3 = classLoader2.getParent();
        System.out.println(classLoader3);

        ClassLoader classLoader4 = String.class.getClassLoader();

        System.out.println(classLoader4);

        System.out.println(classLoader3 == classLoader1);
        System.out.println(classLoader3 == classLoader4);


    }


    //Properties 加载
    @Test
    public void test5() throws IOException {
        //此时 jdbc.properties 文件放在项目文件夹根目录下
        Properties pros = new Properties();
//        FileInputStream fis = new FileInputStream("jdbc.properties");
//        pros.load(fis);
//        System.out.println(pros.getProperty("user")+ " : " + pros.getProperty("password"));


        //使用 classLoader ,默认回到src的下加载文件
        ClassLoader classLoader = ReflectDemo1.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
        pros.load(is);
        System.out.println(pros.getProperty("user") + " : " + pros.getProperty("password"));


    }

    //通过反射 创建对应的运行时类的对象
    @Test
    public void test6() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        Class<Person> pClass = Person.class;

        /*
        想正确使用此方法,要求 空参构造, 且构造方法权限得够 通常这是为public
        对于javabean 要求有public 的空参构造 : 1 便于通过反射创建运行时类对象,2 便于子类继承此运行时类,默认调用super()时有效
         */
        Person obj = pClass.newInstance();
        System.out.println(obj);


        // 创建指定类的对象 classPath 指定类的全类名
        String[] names = {"java.lang.String", "java.util.Date", "com.lx.spring.config.bean.Person"};

        for (String n : names) {
            Class cfn = Class.forName(n);
            System.out.println(cfn);
        }


    }


    //获取当前运行时类的属性结构
    @Test
    public void test7() {
        Class p1Class = P1.class;

        //获取属性结构
        //获取 此类及其父类中声明为public的属性
        Field[] fields = p1Class.getFields();
        for (Field f : fields) {
            System.out.println(f);
        }

        System.out.println();

        //获取当前类声明的所有属性
        Field[] dfs = p1Class.getDeclaredFields();
        for (Field f : dfs) {
            System.out.println(f);

            //1. 权限修饰符
            int m = f.getModifiers();
            System.out.println(m);
            System.out.println(Modifier.toString(m));

            //2. 数据类型
            Class type = f.getType();
            System.out.println(type);
            System.out.println(type.getName());

            //3. 变量名
            String fName = f.getName();
            System.out.println(fName);
            System.out.println();
        }

        //获取运行时类的方法: 类与父类 权限为public的方法
        Method[] methods = p1Class.getMethods();
        for (Method m : methods) {
            System.out.println(m);
        }

        System.out.println();

        // 当前类中定义的所有方法 不包含父类方法
        Method[] dms = p1Class.getDeclaredMethods();
        for (Method m : dms) {
            System.out.println(m);
            //方法的结构
            /*
                @注解
                权限修饰符 返回值类型 方法名(参数类型1 形参名1, ... ) throw xxxException{}
            */
            //获取方法声明的注解
            Annotation[] annos = m.getAnnotations();
            for (Annotation a : annos) {
                System.out.println(a);
                if (a instanceof MyAnnotation) {
                    System.out.println("ma : " + ((MyAnnotation) a).value());
                }
            }

            //权限修饰符
            int mdf = m.getModifiers();
            System.out.println(Modifier.toString(mdf) + " : " + mdf);

            //返回值类型
            Class returnType = m.getReturnType();
            System.out.println(returnType);

            //方法名
            System.out.println(m.getName());

            System.out.print("( ");
            //5.形参列表

            Class[] parameterTypes = m.getParameterTypes();
            if (null == parameterTypes && parameterTypes.length == 0) {

            } else {
                for (Class pc : parameterTypes) {
                    System.out.print(pc + "\t");
                }
            }
            System.out.print(" ) \n");

            // 6.抛出异常
            Class[] exceptionTypes = m.getExceptionTypes();
            if (null == exceptionTypes && exceptionTypes.length == 0) {

            } else {
                for (Class pc : exceptionTypes) {
                    System.out.print(pc + "\t");
                }
            }


            System.out.println("-----------------");

        }

        System.out.println(" cons -----------------");
        //构造器
        Constructor[] cons = p1Class.getConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }

        System.out.println(" dcons -----------------");
        Constructor[] dcons = p1Class.getDeclaredConstructors();
        for (Constructor con : dcons) {
            System.out.println(con);
        }

        System.out.println(" \n");

        // 获取父类
        System.out.println(p1Class.getSuperclass());


        System.out.println(" \n 获取带泛型的父类");
        // 获取带泛型的父类
        Type genericSuperclass = p1Class.getGenericSuperclass();
        System.out.println(genericSuperclass);
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
        System.out.println(actualTypeArguments[0]);
        System.out.println(((Class) actualTypeArguments[0]).getName());


        System.out.println(" \n");
        // 获取接口
        Class[] its = p1Class.getInterfaces();
        for (Class i : its) {
            System.out.println(i);
        }

        System.out.println(" \n");
        // 获取泛型接口
        Type[] igts = p1Class.getGenericInterfaces();
        for (Type i : igts) {

            System.out.println(i);
            if (i instanceof Class) {

            } else {

                ParameterizedType iType = (ParameterizedType) i;
                Type[] iarg = iType.getActualTypeArguments();
                System.out.println(Arrays.toString(iarg));
            }
        }

        System.out.println(" \n 运行时所在的包");
        //
        System.out.println(p1Class.getPackage());


        System.out.println(" \n 运行时声明的注解 \n");
        Annotation[] as = p1Class.getAnnotations();
        for (Annotation a : as) {
            System.out.println(a);
        }

        System.out.println(" \n 运行时声明的注解 \n");
        Annotation[] as1 = p1Class.getDeclaredAnnotations();
        for (Annotation a : as1) {
            System.out.println(a);
        }


        System.out.println("\n  Ole \n");
    }

    //调用运行时类中指定的结构, 属性.方法 构造器
    @Test
    public void test8() throws NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<P1> p1Class = P1.class;

        P1 p = p1Class.newInstance();

        //获取指定属性 要求属性声明为public
        Field id = p1Class.getField("id");
        id.set(p, 1001);

        //要求属性声明为public
//        Field name= p1Class.getField("name");
//        System.out.println(name);

        //获取当前对象的属性值
        Object idv = id.get(p);
        System.out.println(idv);

        //通常使用 declare的方式,避免public属性的限制
        Field name = p1Class.getDeclaredField("name");
        System.out.println(name);
        //private 的属性要先授权才能修改
        name.setAccessible(true);
        name.set(p, "Tom");
        System.out.println(name.get(p));
        System.out.println(Modifier.toString(name.getModifiers()));


        System.out.println(" \n 操作运行时类中指定的方法 \n");
        //操作运行时类中指定的方法
        // getDeclaredMethod() 参数1:指明获取的方法名称 ; 参数2 指明获取的方法的形参列表
        Method show = p1Class.getDeclaredMethod("show", String.class);
        //确保方法可被访问
        show.setAccessible(true);

        //调用方法的invoke() 参数1: 方法的调用对象, 参数2 :方法形参所对应的实参
        // invoke 方法的返回值 即为实际执行的方法的返回值
        Object rs = show.invoke(p, "保健国");
//        Object rs = show.invoke(null, "保健国");
        String rss = (String) rs;
        System.out.println(rs);

        System.out.println(" \n 调用静态方法 \n");
        // private static void showDesc()
        Method showDesc = p1Class.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
//        Object srd = showDesc.invoke(p1Class);
        Object srd = showDesc.invoke(null);
        System.out.println(srd);


        System.out.println(" \n 调用构造器方法 \n");
        Constructor<P1> constructor = p1Class.getDeclaredConstructor(String.class);

        constructor.setAccessible(true);

        P1 p1 = constructor.newInstance("Jerry");
        System.out.println(p1);


    }


}

class A {
    static {
        m = 300;
    }

    static int m = 100;
}

class Creature<T> implements Serializable {

    private char gender;
    public double weight;

    private void breath() {
        System.out.println("生物呼吸");
    }

    public void eat() {

    }

}

interface MyInterface {
    void info();
}

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    String value() default "乔杉2";
}

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.CLASS)
@interface MyAnnotation1 {
    String value() default "乔杉2";
}

@MyAnnotation(value = "P1a")
@MyAnnotation1
class P1 extends Creature<String> implements Comparable<String>, MyInterface {
    private String name;
    int age;
    public int id;

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("name", name)
                .append("age", age)
                .append("id", id)
                .toString();
    }

    public P1() {
    }

    @MyAnnotation(value = "abc")
    private P1(String name) {
        this.name = name;
    }


    P1(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @MyAnnotation
    private String show(String nation) {
        System.out.println("我的国籍: " + nation);
        return nation;
    }

    public String display(String interests, int a) throws NullPointerException {
        System.out.println("display : " + interests);
        return interests;
    }


    public void info() {
        System.out.println("我是一个人");
    }

    public int compareTo(String o) {
        return 0;
    }

    private static void showDesc(){
        System.out.println("mao xian");
    }
}
